package main

import (
	"bytes"
	"db-monitor/database"
	"encoding/json"
	"flag"
	"fmt"
	"log/slog"
	"os"

	"github.com/fsnotify/fsnotify"
	"github.com/natefinch/lumberjack"
	"github.com/spf13/viper"
)

const DefaultConfig = `
logs:
  # slog 日志等级
  # LevelDebug Level = -4
  #	LevelInfo  Level = 0
  #	LevelWarn  Level = 4
  #	LevelError Level = 8
  level: 0
  # 日志目录路径
  path: "logs"
  # 文件最大大小, M
  max-size: 50
  # 备份数
  max-backups: 100
  # 存放时间, 天
  max-age: 30
  # 是否压缩
  compress: false

database:
  driver: "oracle"
  source: "oracle://user:pass@server/service_name"
`

// 全局配置变量
var Conf = new(config)

type config struct {
	Logs     *LogsConfig `mapstructure:"logs" json:"logs"`
	Database *Database   `mapstructure:"database" json:"database"`
}

type LogsConfig struct {
	Level      slog.Level `mapstructure:"level" json:"level"`
	Path       string     `mapstructure:"path" json:"path"`
	MaxSize    int        `mapstructure:"max-size" json:"maxSize"`
	MaxBackups int        `mapstructure:"max-backups" json:"maxBackups"`
	MaxAge     int        `mapstructure:"max-age" json:"maxAge"`
	Compress   bool       `mapstructure:"compress" json:"compress"`
}

type Database struct {
	Driver string `mapstructure:"driver" json:"driver"`
	Source string `mapstructure:"source" json:"source"`
}

// 设置读取配置信息
func LoadConfig() {
	viper.SetConfigName("config")
	viper.SetConfigType("yml")
	viper.AddConfigPath("/etc/db-monitor/")   // 查找配置文件所在的路径
	viper.AddConfigPath("$HOME/.db-monitor/") // 多次调用以添加多个搜索路径
	viper.AddConfigPath(".")                  // 还可以在工作目录中查找配置
	// 读取配置信息
	if err := viper.ReadInConfig(); err == nil {
		//fmt.Println("加载配置文件：", viper.ConfigFileUsed())

		// 热更新配置
		viper.WatchConfig()
		viper.OnConfigChange(func(e fsnotify.Event) {
			// 将读取的配置信息保存至全局变量Conf
			if err := viper.Unmarshal(Conf); err != nil {
				panic(fmt.Errorf("解析配置文件失败: %s", err))
			}
		})

	} else {
		// 找不到配置文件时加载默认配置
		viper.ReadConfig(bytes.NewBuffer([]byte(DefaultConfig)))
	}

	// 将读取的配置信息保存至全局变量Conf
	if err := viper.Unmarshal(Conf); err != nil {
		panic(fmt.Errorf("解析配置文件失败: %s", err))
	}

	// 部分配合通过环境变量加载
	dbDriver := os.Getenv("DB_DRIVER")
	if dbDriver != "" {
		Conf.Database.Driver = dbDriver
	}
}

func main() {
	// 读取系统配置文件config.yml 读取路径依次为：
	// 1. /etc/db-monitor/
	// 2. $HOME/.db-monitor/
	// 3. db-monitor启动目录
	LoadConfig()

	// 全局日志记录器，默认使用slog.
	jackLogger := &lumberjack.Logger{
		Filename:   fmt.Sprintf("%s/db-monitor.log", Conf.Logs.Path),
		LocalTime:  true,
		MaxSize:    Conf.Logs.MaxSize, // megabytes
		MaxBackups: Conf.Logs.MaxBackups,
		MaxAge:     Conf.Logs.MaxAge,   //days
		Compress:   Conf.Logs.Compress, // disabled by default
	}
	logger := slog.New(slog.NewJSONHandler(jackLogger, &slog.HandlerOptions{
		AddSource:   true,            // 记录日志位置
		Level:       Conf.Logs.Level, // 设置日志级别
		ReplaceAttr: nil,
	}))
	slog.SetDefault(logger)

	client, err := database.NewDBClient(Conf.Database.Driver, Conf.Database.Source)
	if err != nil {
		slog.Error(fmt.Sprintf("Can't open the driver: %s", err))
		return
	}

	defer func() {
		err = client.Close()
		if err != nil {
			slog.Error(fmt.Sprintf("Can't close connection: %s", err))
		}
	}()
	var querySql string
	flag.StringVar(&querySql, "sql", "select 1 from dual", "请输入数据库查询语句【SQL】")
	flag.Parse()
	result, err := client.RetrieveMap(querySql)
	if err != nil {
		slog.Error(fmt.Sprintf("Can't get map result from database: %s", err))
		return
	}
	//切片转json
	jsonBytes, err := json.Marshal(result)
	if err != nil {
		slog.Error(fmt.Sprintf("Can't switch to json: %s", err))
		return
	}
	slog.Info(string(jsonBytes))
	fmt.Println(string(jsonBytes))
}
